package genetic;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public abstract class Phenotype {

	public abstract double fitness(Genotype g);
	public abstract Genotype createRandomInstance();
	public abstract Genotype[] crossover(Genotype g1, Genotype g2);
	public abstract Genotype mutate(Genotype g, double Pm);
	public abstract List<Genotype> getMatingPool(List<Genotype> population, int populationSize);



	protected boolean[] getRandomBitString(int size, double oneProbability){
		boolean[] ans = new boolean[size];
		for (int i=0; i<ans.length; i++){
			if (Math.random()>oneProbability){
				ans[i] = false;
			} else {
				ans[i] = true;
			}
		}
		return ans;
	}

	protected boolean[] bitwiseMutation(boolean[] bitString, double Pm){
		boolean[] ans = Arrays.copyOf(bitString, bitString.length);				//create a copy so we don't harm the original
		for (int i=0; i<ans.length; i++){
			double rand = Math.random();
			if (rand<Pm){
				ans[i] = !ans[i];
			}
		}
		return ans;
	}

	protected boolean[][] singlePointCrossOver(boolean[] g1rep, boolean[] g2rep){
		int crossoverPoint = (int)(Math.random()*(g1rep.length-1)+1);
		boolean[] os1rep = new boolean[g1rep.length];
		boolean[] os2rep = new boolean[g2rep.length];
		for (int i=0; i<g1rep.length ; i++){
			os1rep[i] =  (i<crossoverPoint ? g1rep[i] : g2rep[i]);
			os2rep[i] =  (i<crossoverPoint ? g2rep[i] : g1rep[i]);
		}
		return new boolean[][]{os1rep, os2rep};
	}

	protected List<Genotype> getMatingPoolRouletteWheel(List<Genotype> population, int populationSize){	//TODO rounding is needed to overcome double errors
		List<Genotype> matingPool = new LinkedList<Genotype>();

		double fitnessSum = 0;
		for (Genotype g: population){
			fitnessSum = fitnessSum + fitness(g);
		}
		for (int i=0; i<populationSize; i++){	//creating the mating pool
			double rand = Math.random()*fitnessSum;
			for (Genotype g: population){
				rand = rand - fitness(g);
				if (rand<=0){
					matingPool.add(g);
					break;
				}
			}
		}
		Collections.shuffle(matingPool);
		return matingPool;
	}

}
